/*
 * Copyright (c) 2001 Sun Microsystems, Inc.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *       Sun Microsystems, Inc. for Project JXTA."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact Project JXTA at http://www.jxta.org.
 *
 * 5. Products derived from this software may not be called "JXTA",
 *    nor may "JXTA" appear in their name, without prior written
 *    permission of Sun.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of Project JXTA.  For more
 * information on Project JXTA, please see
 * <http://www.jxta.org/>.
 *
 * This license is based on the BSD license adopted by the Apache Foundation.
 *
 * $Id: ContentIdImpl.java,v 1.2 2005/05/12 16:20:10 hamada Exp $
 *
 */

package net.jxta.share;

import java.io.InputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * ContentId implementation based on MD5 digest algorithm.
 */
public class ContentIdImpl implements ContentId {
    private byte[] bytes;

    // content id type
    private static final String ID_TYPE = "md5";

    /*
     * Creates a new content id from the specified identifier string.
     * The identifier string is of the form <idtype> ":" <hexstring>
     * where <idtype> is the identifier type (currently 'md5') and
     * <hexstring> the hash hex bytes.
     */
    public ContentIdImpl(String s) {
	bytes = parse(s);
    }

    /*
     * Creates a new content id for the bytes contained in the specified
     * input stream.
     */
    public ContentIdImpl(InputStream is) throws IOException {
	MessageDigest md;
	try {
	    md = MessageDigest.getInstance("MD5");
	} catch (NoSuchAlgorithmException e) {
	    throw new IllegalStateException(
		"Could not find MD5 MessageDigest");
	}
	byte[] b = new byte[1024];
	int len;
	while ((len = is.read(b, 0, b.length)) != -1) {
	    md.update(b, 0, len);
	}
	bytes = md.digest();
    }

    // Parse content id string
    private byte[] parse(String s) {
	int pos = s.indexOf(':');
	if (pos == -1) {
	    throw new IllegalArgumentException("Missing content id type: " + s);
	}
	String type = s.substring(0, pos);
	if (!type.equalsIgnoreCase(ID_TYPE)) {
	    throw new IllegalArgumentException("Invalid content id type: " + s);
	}
	if (s.length() - pos - 1 != 32) {
	    throw new IllegalArgumentException(
		"Invalid content id hash length: " + s);
	}
	s = s.substring(pos + 1);
	byte[] b = new byte[16];
	for (int i = 0, j = 0; i < 16; i++) {
	    int hi = Character.digit(s.charAt(j++), 16);
	    int lo = Character.digit(s.charAt(j++), 16);
	    if (hi == -1 || lo == -1) {
		throw new IllegalArgumentException(
		    "Invalid content id hash: " + s);
	    }
	    b[i] = (byte)((hi << 4) | lo);
	}
	return b;
    }

    /**
     * Returns the hash code for this content id.
     */
    public int hashCode() {
	byte[] b = bytes;
	return ((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) |
	       ((b[2] & 0xff) <<  8) | ((b[3] & 0xff) <<  0);
    }

    /**
     * Returns true if this content id is equal to the specified object.
     */
    public boolean equals(Object obj) {
	if (this == obj) {
	    return true;
	}
	if (!(obj instanceof ContentIdImpl)) {
	    return false;
	}
	ContentIdImpl id = (ContentIdImpl)obj;
	byte[] b1 = bytes;
	byte[] b2 = id.bytes;
	if (b1.length != b2.length) {
	    return false;
	}
	for (int i = 0; i < b1.length; i++) {
	    if (b1[i] != b2[i]) {
		return false;
	    }
	}
	return true;
    }

    /**
     * Returns the string representation of this content id.
     */
    public String toString() {
	byte[] b = bytes;
	StringBuffer sb = new StringBuffer(ID_TYPE.length() + 1 + b.length * 2);
	sb.append(ID_TYPE);
	sb.append(':');
	for (int i = 0; i < b.length; i++) {
	    int hi = (b[i] >> 4) & 0xf;
	    int lo = b[i] & 0xf;
	    sb.append(Character.forDigit(hi, 16));
	    sb.append(Character.forDigit(lo, 16));
	}
	return sb.toString();
    }
}
